import * as React from 'react';
import PropTypes from 'prop-types';
import Icon from '../icon';
import classNames from '../../utils/classnames';
import deprecationWarning from '../../utils/deprecationWarning';

/**
 * weui style uploader
 *
 */
interface Lang {
    maxError: (value: number) => void;
}
interface MyFile extends File {
    lastModifiedDate: Date;
    error?: any;
    url?: string;
    status: any;
    onClick?: () => void;
    video: string;
}
interface UploaderProps {
    files: Array<MyFile>;
    lang: Lang;
    maxCount: number;
    maxWidth: number;
    onChange?: (file: File, event?: any) => void;
    onError: (error: any) => void;
    className?: any;
    onFileClick?: (e?: any, file?: File, idx?: any) => void;
    maxsize?: number;
    onOversize: (val: number) => void;
    type: 'image' | 'video';
    onDelete: (file: File, id: number) => void;
    currentVideo: (val: string) => void;
    showTitle: boolean;
    size: 'small' | 'normal' | 'large';
    showAddInput: boolean;
    style?: React.CSSProperties;
}
type CustomFile = {
    nativeFile: Blob;
    lastModified: number;
    lastModifiedDate: Date;
    data: string;
    name: string;
    size: number;
    type: string;
};
type HandleFileCallback = (
    file: CustomFile | Blob,
    e: RenderOnloadEvent,
) => void;
type RenderOnloadEvent = {
    target: { result: any };
};
export default class Uploader extends React.Component<UploaderProps> {
    private uploaderRef = React.createRef<HTMLInputElement>();

    static propTypes = {
        /**
         * max amount of allow file
         *
         */
        maxCount: PropTypes.number,
        /**
         * maxWidth of image for uploader to compress
         *
         */
        maxWidth: PropTypes.number,
        /**
         * 文件大小上限(单位：M)
         *
         */
        maxsize: PropTypes.number,
        /**
         * when file change, pass property `(event, file)`
         *
         */
        onChange: PropTypes.func,
        /**
         * when there is error, pass property `msg`
         *
         */
        onError: PropTypes.func,
        /**
         * 文件大小超出限制触发
         *
         */
        onOversize: PropTypes.func,
        /**
         * 删除文件触发，参数为file和id
         *
         */
        onDelete: PropTypes.func,
        /**
         * array of photos thumbnails to indicator status, include property `url`, `status`, `error`
         *
         */
        files: PropTypes.array,
        /**
         * languages object, with property `maxError`
         *
         */
        lang: PropTypes.object,
        /**
         * 接收文件类型(取值为'image'时上传图片，为'video'时上传视频)
         *
         */
        type: PropTypes.string,
        /**
         * 参数为当前视频src
         *
         */
        currentVideo: PropTypes.func,
        /**
         * 是否展示标题
         *
         */
        showTitle: PropTypes.bool,
        /**
         * 图片和视频的预览图宽高:small|normal|large
         *
         */
        size: PropTypes.string,
        /**
         * 是否显示上传文件的按钮
         *
         */
        showAddInput: PropTypes.bool,
    };

    static defaultProps = {
        maxCount: 4 as UploaderProps['maxCount'],
        maxWidth: 500 as UploaderProps['maxWidth'],
        files: [] as UploaderProps['files'],
        onChange: undefined as UploaderProps['onChange'],
        onError: (undefined as unknown) as UploaderProps['onError'],
        onOversize: (undefined as any) as UploaderProps['onOversize'],
        onDelete: (undefined as any) as UploaderProps['onDelete'],
        lang: {
            maxError: (maxCount) => `最多只能上传${maxCount}张图片`,
        } as UploaderProps['lang'],
        type: 'image' as UploaderProps['type'],
        currentVideo: (undefined as unknown) as UploaderProps['currentVideo'],
        showTitle: false as UploaderProps['showTitle'],
        size: 'normal' as UploaderProps['size'],
    };

    getImageSize() {
        let size;
        if (this.props.size === 'small') {
            size = 57;
        } else if (this.props.size === 'normal') {
            size = 76;
        } else if (this.props.size === 'large') {
            size = 106;
        } else {
            return 76;
        }
        return size;
    }

    /**
     * Detecting vertical squash in loaded image.
     * Fixes a bug which squash image vertically while drawing into canvas for some images.
     * This is a bug in iOS6 devices. This function from https://github.com/stomita/ios-imagefile-megapixel
     * With react fix by n7best
     */

    // eslint-disable-next-line class-methods-use-this
    detectVerticalSquash(img: any) {
        let data;
        const ih = img.naturalHeight;
        const canvas = document.createElement('canvas');
        canvas.width = 1;
        canvas.height = ih;
        const ctx = canvas.getContext('2d');
        if (!ctx) return;
        ctx.drawImage(img, 0, 0);
        try {
            // Prevent cross origin error
            data = ctx.getImageData(0, 0, 1, ih).data;
        } catch (err) {
            // hopeless, assume the image is well and good.
            console.log('Cannot check verticalSquash: CORS?');
            // eslint-disable-next-line consistent-return
            return 1;
        }
        // search image edge pixel position in case it is squashed vertically.
        let sy = 0;
        let ey = ih;
        let py = ih;
        while (py > sy) {
            const alpha = data[(py - 1) * 4 + 3];
            if (alpha === 0) {
                ey = py;
            } else {
                sy = py;
            }
            // eslint-disable-next-line no-bitwise
            py = (ey + sy) >> 1;
        }
        const ratio = py / ih;
        // eslint-disable-next-line consistent-return
        return ratio === 0 ? 1 : ratio;
    }

    handleFile(file: Blob, cb: HandleFileCallback) {
        let reader: any;
        if (typeof FileReader !== 'undefined') {
            reader = new FileReader();
        } else if (window.FileReader) {
            reader = new window.FileReader();
        }

        reader.onload = (e: RenderOnloadEvent) => {
            if (/image/g.test(file.type)) {
                let img: any;
                if (typeof img !== 'undefined') {
                    img = new Image();
                } else if (window.Image) {
                    img = new window.Image();
                }
                img.onload = () => {
                    const w = Math.min(this.props.maxWidth, img.width);
                    const h = img.height * (w / img.width);
                    const canvas = document.createElement('canvas');
                    const ctx = canvas.getContext('2d');
                    if (ctx) {
                        const { drawImage } = ctx;
                        const newDrawImage = (
                            _img: CanvasImageSource,
                            sx: number,
                            sy: number,
                            sw: number,
                            sh: number,
                            dx?: number,
                            dy?: number,
                            dw?: number,
                            dh?: number,
                        ) => {
                            let vertSquashRatio = 1;
                            let newSw = sw;
                            let newSh = sh;
                            if (
                                !!_img &&
                                (_img as HTMLImageElement).nodeName === 'IMG'
                            ) {
                                vertSquashRatio = this.detectVerticalSquash(
                                    _img,
                                ) as number;
                                if (typeof newSw === 'undefined')
                                    newSw = (_img as HTMLImageElement)
                                        .naturalWidth;
                                if (typeof newSh === 'undefined')
                                    newSh = (_img as HTMLImageElement)
                                        .naturalHeight;
                            }
                            if (arguments.length === 9)
                                drawImage.call(
                                    ctx,
                                    _img,
                                    sx,
                                    sy,
                                    newSw,
                                    newSh,
                                    dx,
                                    dy,
                                    dw,
                                    dh! / vertSquashRatio,
                                );
                            else if (typeof newSw !== 'undefined')
                                drawImage.call(
                                    ctx,
                                    _img,
                                    sx,
                                    sy,
                                    newSw,
                                    newSh / vertSquashRatio,
                                );
                            else drawImage.call(ctx, _img, sx, sy);
                        };

                        canvas.width = w;
                        canvas.height = h;
                        newDrawImage(img, 0, 0, w, h);

                        const base64 = canvas.toDataURL('image/png');

                        cb(
                            {
                                nativeFile: file,
                                lastModified: (file as MyFile).lastModified,
                                lastModifiedDate: (file as MyFile)
                                    .lastModifiedDate,
                                name: (file as MyFile).name,
                                size: file.size,
                                type: file.type,
                                data: base64,
                            },
                            e,
                        );
                    } else {
                        cb(file, e);
                    }
                };
                img.src = e.target.result;
            } else if (/video/g.test(file.type)) {
                const video = document.createElement('video');
                video.src = e.target.result;
                video.width = this.getImageSize();
                video.height = this.getImageSize();
                video.controls = true;
                video.muted = true;
                video.autoplay = true;
                video.preload = 'preload';
                this.props.currentVideo(e.target.result);
                video.addEventListener('loadeddata', function () {
                    const canvas = document.createElement('canvas');
                    const ctx = canvas.getContext('2d');
                    if (ctx) {
                        canvas.width = this.videoWidth;
                        canvas.height = this.videoHeight;
                        ctx.drawImage(this, 0, 0, 600, 600);
                        const base64 = canvas.toDataURL('image/png');
                        cb(
                            {
                                nativeFile: file,
                                lastModified: (file as MyFile).lastModified,
                                lastModifiedDate: (file as MyFile)
                                    .lastModifiedDate,
                                name: (file as MyFile).name,
                                size: file.size,
                                type: file.type,
                                data: base64,
                            },
                            e,
                        );
                    } else {
                        cb(file, e);
                    }
                });
            }
        };
        reader.readAsDataURL(file);
    }

    handleChange(e: React.ChangeEvent<HTMLInputElement>) {
        const langs = this.props.lang;
        const { files } = e.target;

        if (!files || files.length === 0) return;

        if (this.props.files.length >= this.props.maxCount) {
            this.props.onError(langs.maxError(this.props.maxCount));
            return;
        }
        // eslint-disable-next-line no-restricted-syntax
        for (const key in files) {
            if (files.hasOwnProperty(key)) {
                const file = files[key];
                const maxsize =
                    this.props.maxsize ||
                    (this.props.type === 'image' ? 2 : 10);
                if (file.size / (1024 * 1024) > maxsize) {
                    this.props.onOversize(file.size);
                    return;
                }
                this.handleFile(file, (_file: File, _e: RenderOnloadEvent) => {
                    if (this.props.onChange) this.props.onChange(_file, _e);
                    this.uploaderRef.current!.value = '';
                });
            }
        }
    }

    renderFiles() {
        return this.props.files.map((file, idx) => {
            const imgSrc =
                '';
            const closeSrc =
                '';
            const { url, error, status, onClick, ...others } = file;
            const wrapStyle: React.CSSProperties = {
                position: 'relative',
                marginRight: 12,
                marginBottom: 9,
                float: 'left',
                width: this.getImageSize(),
                height: this.getImageSize(),
            };
            const fileStyle: React.CSSProperties = {
                backgroundImage: `url(${url})`,
                position: 'relative',
                width: this.getImageSize(),
                height: this.getImageSize(),
            };
            const videofileStyle: React.CSSProperties = {
                backgroundImage: `url(${url})`,
                filter: 'contrast(0.4)',
                width: this.getImageSize(),
                height: this.getImageSize(),
            };
            const iconStyle: React.CSSProperties = {
                position: 'absolute',
                right: '-9px',
                top: '-9px',
                width: '16px',
                height: '16px',
            };
            const btnStyle: React.CSSProperties = {
                position: 'absolute',
                top: '50%',
                left: '50%',
                transform: 'translate(-50%, -50%)',
                width: '22px',
                height: '22px',
            };
            const cls = classNames({
                'weui-uploader__file': true,
                'weui-uploader__file_status': error || status,
            });

            if (onClick) {
                deprecationWarning(
                    'File onClick',
                    'Uploader onFileClick',
                    null,
                );
            }

            const handleFileClick =
                onClick ||
                ((e: any) => {
                    if (this.props.onFileClick)
                        this.props.onFileClick(e, file, idx);
                });
            const handleClick = (e: any) => {
                e.stopPropagation();
                if (this.props.onDelete) this.props.onDelete(file, idx);
            };
            if (this.props.type === 'image') {
                return (
                    <div style={wrapStyle} key={idx}>
                        <li
                            className={cls}
                            key={idx}
                            style={fileStyle}
                            onClick={handleFileClick}
                            {...others}
                        >
                            {error || status ? (
                                <div className="weui-uploader__file-content">
                                    {error ? <Icon value="warn" /> : status}
                                </div>
                            ) : (
                                false
                            )}
                        </li>
                        <img
                            src={closeSrc}
                            style={iconStyle}
                            onClick={handleClick}
                            alt=""
                        />
                    </div>
                );
            }
            return (
                <div style={wrapStyle} key={idx}>
                    <li
                        className={cls}
                        key={idx}
                        style={videofileStyle}
                        onClick={handleFileClick}
                        {...others}
                    >
                        {error || status ? (
                            <div className="weui-uploader__file-content">
                                {error ? <Icon value="warn" /> : status}
                            </div>
                        ) : (
                            false
                        )}
                    </li>
                    <img
                        src={closeSrc}
                        style={iconStyle}
                        onClick={handleClick}
                        alt=""
                    />
                    <img src={imgSrc} style={btnStyle} alt="" />
                </div>
            );
        });
    }

    render() {
        const {
            className,
            maxCount,
            files,
            onChange,
            onFileClick,
            lang,
            onOversize,
            type,
            onDelete,
            currentVideo,
            showTitle,
            size,
            showAddInput,
            ...others
        } = this.props;
        const inputProps = { ...others };
        delete inputProps.onError;
        delete inputProps.maxWidth;

        const cls = classNames({
            'weui-uploader': true,
            [className]: className,
        });

        return (
            <div className={cls}>
                {showTitle ? (
                    <div className="weui-uploader__hd">
                        <div className="weui-uploader__info">
                            {files.length}/{maxCount}
                        </div>
                    </div>
                ) : null}
                <div
                    className="weui-uploader__bd"
                    style={{ overflow: 'visible' }}
                >
                    <ul className="weui-uploader__files">
                        {this.renderFiles()}
                    </ul>
                    <div
                        style={{
                            width: this.getImageSize() - 2,
                            height: this.getImageSize() - 2,
                            display: showAddInput === true ? 'block' : 'none',
                        }}
                        className="weui-uploader__input-box"
                    >
                        <input
                            ref={this.uploaderRef} //let react to reset after onchange
                            className="weui-uploader__input"
                            type="file"
                            accept={type === 'image' ? 'image/*' : 'video/*'}
                            onChange={this.handleChange.bind(this)}
                            {...inputProps}
                        />
                    </div>
                </div>
            </div>
        );
    }
}
